﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

using RexToy.ExpressionLanguage;
using RexToy.UI.WinForm.Pmlx;

namespace RexToy.UI.WinForm.Controls
{
    public class TreeView : System.Windows.Forms.TreeView, ITemplateItem
    {
        private PmlxNode _node;
        private Type _nodeType;
        private string _recursiveItemName;
        private RecursiveType _recursiveItemType;
        private ExpressionLanguageEngine _el;
        public TreeView()
        {
        }

        public void Bind(object data)
        {
            //TODO: if data itself is a collection??
            var c = data as IEnumerable;
            if (c == null)
            {
                BindSingle(data);
            }
            else
            {
                BindCollection(c);
            }
        }

        private void BindSingle(object data)
        {
            IReflector rData = Reflector.Bind(data);
            var root = Activator.CreateInstance(_nodeType) as TreeNode;
            _el.Assign(ControlConstant.EVAL_CTX_ITEM, data);
            IReflector rNode = Reflector.Bind(root);
            foreach (var attr in _node.Attributes)
                attr.ApplyValue(rNode);
            this.Nodes.Add(root);

            object item = rData.GetPropertyOrFieldValue(_recursiveItemName, false);
            _Bind(item, root);
        }

        private void BindCollection(IEnumerable collection)
        {
            foreach (object data in collection)
            {
                var node = Activator.CreateInstance(_nodeType) as TreeNode;
                _el.Assign(ControlConstant.EVAL_CTX_ITEM, data);

                IReflector rNode = Reflector.Bind(node);
                foreach (var attr in _node.Attributes)
                    attr.ApplyValue(rNode);
                this.Nodes.Add(node);

                IReflector rData = Reflector.Bind(data);
                object item = rData.GetPropertyOrFieldValue(_recursiveItemName, false);
                _Bind(item, node);
            }
        }

        private void _Bind(object item, TreeNode parent)
        {
            if (item == null)
                return;

            switch (_recursiveItemType)
            {
                case RecursiveType.Single:
                    _BindSingle(item, parent);
                    break;

                case RecursiveType.Collection:
                    var coll = item as IEnumerable;
                    _BindCollection(coll, parent);
                    break;
            }
        }

        private void _BindSingle(object item, TreeNode parent)
        {
            IReflector rData = Reflector.Bind(item);
            var node = Activator.CreateInstance(_nodeType) as TreeNode;
            _el.Assign(ControlConstant.EVAL_CTX_ITEM, item);
            IReflector rNode = Reflector.Bind(node);
            foreach (var attr in _node.Attributes)
                attr.ApplyValue(rNode);
            parent.Nodes.Add(node);

            var sub = rData.GetPropertyOrFieldValue(_recursiveItemName);
            if (sub != null)
                _BindSingle(sub, node);
        }

        private void _BindCollection(IEnumerable collection, TreeNode parent)
        {
            foreach (object data in collection)
            {
                var node = Activator.CreateInstance(_nodeType) as TreeNode;
                _el.Assign(ControlConstant.EVAL_CTX_ITEM, data);

                IReflector rNode = Reflector.Bind(node);
                foreach (var attr in _node.Attributes)
                    attr.ApplyValue(rNode);
                parent.Nodes.Add(node);

                IReflector rData = Reflector.Bind(data);
                IEnumerable item = rData.GetPropertyOrFieldValue(_recursiveItemName, false) as IEnumerable;
                _BindCollection(item, node);
            }
        }

        public void ParseTemplate(PmlxNode template)
        {
            _el = template.OwnerDocument.ELEngine;
            var p = template.NavigateToSingle("Recursive");
            _recursiveItemName = p.Accessor.GetStringValue("@RecursiveName");
            _recursiveItemType = p.Accessor.GetEnumValue<RecursiveType>("@RecursiveType") ?? RecursiveType.Single;

            _node = p.ChildNodes.Single();
            _nodeType = _node.MakeType();
        }
    }
}
